package com.ziq.zcamp.util

import com.blankj.utilcode.constant.TimeConstants
import com.ziq.zcamp.R
import com.ziq.zcamp.manager.GlobalContextManager
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.abs

class TimeUtil {

    companion object {

        val TIME_FORMATTER = SimpleDateFormat("HH:mm", Locale.US)

        val MM_dd_FORMATTER_CN = SimpleDateFormat("M月d日", Locale.US)
        val MM_dd_time_FORMATTER_CN = SimpleDateFormat("M月d日 HH:mm", Locale.US)
        val yyyy_MM_DATE_FORMATTER_CN = SimpleDateFormat("yyyy年M月", Locale.US)
        val yyyy_MM_dd_DATE_FORMATTER_CN = SimpleDateFormat("yyyy年M月d日", Locale.US)
        val yyyy_MM_dd_time_DATE_FORMATTER_CN = SimpleDateFormat("yyyy年M月d日 HH:mm", Locale.US)

        val MM_dd_FORMATTER = SimpleDateFormat("MM-dd", Locale.US)
        val MM_dd_time_FORMATTER = SimpleDateFormat("MM-dd HH:mm", Locale.US)
        val yyyy_MM_DATE_FORMATTER = SimpleDateFormat("yyyy-MM", Locale.US)
        val yyyy_MM_dd_DATE_FORMATTER = SimpleDateFormat("yyyy-MM-dd", Locale.US)
        val yyyy_MM_dd_time_DATE_FORMATTER = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US)
        val yyyy_MM_dd_time_ss_DATE_FORMATTER = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)

        val yyyy_MM_dd_SLASH_FORMATTER = SimpleDateFormat("yyyy/MM/dd", Locale.US)
        val yyyy_MM_dd_SPOT_FORMATTER_CN = SimpleDateFormat("yyyy.MM.dd", Locale.US)
        val yyyy_MM_dd_time_SPOT_FORMATTER_CN = SimpleDateFormat("yyyy.MM.dd HH:mm", Locale.US)
        val MM_dd_SPOT_FORMATTER_CN = SimpleDateFormat("MM.dd", Locale.US)
        val MM_dd_SLASH_FORMATTER = SimpleDateFormat("MM/dd", Locale.US)


        fun getDayBetween(startTimeInMillis: Long, endTimeInMillis: Long): Long {
            val t1 = getDateTimeClearHMS(startTimeInMillis)
            val t2 = getDateTimeClearHMS(endTimeInMillis)
            return abs(t1 - t2) /  TimeConstants.DAY
        }

        fun getTime(timeInMillis: Long, dateFormat: SimpleDateFormat): String? {
            return dateFormat.format(Date(timeInMillis))
        }

        fun getPublishTime(timeInMillis: Long): String? {
            if(isSameYear(timeInMillis, Calendar.getInstance().timeInMillis)){
                return MM_dd_time_FORMATTER.format(Date(timeInMillis))
            } else {
                return yyyy_MM_dd_time_DATE_FORMATTER.format(Date(timeInMillis))
            }
        }

        fun getWithdrawTime(timeInMillis: Long): String? {
            if(isSameYear(timeInMillis, Calendar.getInstance().timeInMillis)){
                return MM_dd_time_FORMATTER_CN.format(Date(timeInMillis))
            } else {
                return yyyy_MM_dd_time_DATE_FORMATTER.format(Date(timeInMillis))
            }
        }

        fun getMessageTime(timeInMillis: Long): String? {
            if(isToday(Date(timeInMillis))){
                return TIME_FORMATTER.format(Date(timeInMillis))
            } else if(isSameYear(timeInMillis, Calendar.getInstance().timeInMillis)){
                return MM_dd_FORMATTER_CN.format(Date(timeInMillis))
            } else {
                return yyyy_MM_dd_DATE_FORMATTER_CN.format(Date(timeInMillis))
            }
        }

        fun getCampDayTime(startTimeInMillis: Long?, endTimeInMillis: Long?): String{
            val stringBuilder = StringBuilder()
            try {
                if(startTimeInMillis != null && startTimeInMillis > 0){
                    stringBuilder.append(yyyy_MM_dd_SPOT_FORMATTER_CN.format(Date(startTimeInMillis)))
                    if(endTimeInMillis != null && endTimeInMillis > 0 && startTimeInMillis != endTimeInMillis){
                        stringBuilder.append("-")
                        if(isSameYear(startTimeInMillis, endTimeInMillis)){
                            stringBuilder.append(MM_dd_SPOT_FORMATTER_CN.format(Date(endTimeInMillis)))
                        } else {
                            stringBuilder.append(yyyy_MM_dd_SPOT_FORMATTER_CN.format(Date(endTimeInMillis)))
                        }
                    }
                }
            }catch (e:Exception){
            }
            return stringBuilder.toString()
        }

        fun isSameYear(timeInMillis1: Long, timeInMillis2: Long) : Boolean{
            val calendar1 = Calendar.getInstance()
            calendar1.timeInMillis = timeInMillis1
            val calendar2 = Calendar.getInstance()
            calendar2.timeInMillis = timeInMillis2
            return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR)
        }

        fun isSameDay(timeInMillis1: Long, timeInMillis2: Long) : Boolean{
            if (!isSameYear(timeInMillis1,timeInMillis2))
                return false
            val calendar1 = Calendar.getInstance()
            calendar1.timeInMillis = timeInMillis1
            val calendar2 = Calendar.getInstance()
            calendar2.timeInMillis = timeInMillis2
            return calendar1.get(Calendar.DAY_OF_YEAR) == calendar2.get(Calendar.DAY_OF_YEAR)
        }

        fun isToday(date:Date) : Boolean{
            val calendar1 = Calendar.getInstance()
            val calendar2 = Calendar.getInstance()
            calendar2.time = date
            return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR) &&
                    calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH) &&
                    calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH)
        }

        fun getBirthday(date:Date) : String{
            val calendar = Calendar.getInstance()
            calendar.time = date
            return GlobalContextManager.getString(R.string.birthday_format, calendar.get(Calendar.YEAR), (calendar.get(Calendar.MONTH) + 1), calendar.get(Calendar.DAY_OF_MONTH))
        }



        fun getWeekDay(date:Date) : String{
            val calendar = Calendar.getInstance()
            calendar.time = date
            var dateIndex = calendar.get(Calendar.DAY_OF_WEEK)
            when(dateIndex){
                1 -> return "周日"
                2 -> return "周一"
                3 -> return "周二"
                4 -> return "周三"
                5 -> return "周四"
                6 -> return "周五"
                7 -> return "周六"
            }
            return ""
        }

        fun getAge(timeInMillis :Long?) : Int{
            if(timeInMillis == null){
                return 0
            }
            return getAge(Date(timeInMillis))
        }

        fun getAge(birthDay:Date) : Int{
            val cal = Calendar.getInstance()
            if (cal.before(birthDay)) { //出生日期晚于当前时间，无法计算
                return 0
            }
            val yearNow = cal.get(Calendar.YEAR);  //当前年份
            val monthNow = cal.get(Calendar.MONTH);  //当前月份
            val dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH); //当前日期
            cal.setTime(birthDay);
            val yearBirth = cal.get(Calendar.YEAR);
            val monthBirth = cal.get(Calendar.MONTH);
            val dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
            var age = yearNow - yearBirth;   //计算整岁数
            if (monthNow <= monthBirth) {
                if (monthNow == monthBirth) {
                    if (dayOfMonthNow < dayOfMonthBirth){
                        age--
                    }  //当前日期在生日之前，年龄减一
                }else{
                    age--//当前月份在生日之前，年龄减一
                }
            }
            return age;
        }


        fun getCommentTime(timeInMillis :Long?) : String{
            var result = ""
            if(timeInMillis?:0 <= 0){
                return result
            }
            if(timeInMillis != null){
                val currentTime = Calendar.getInstance().timeInMillis
                if(isSameYear(timeInMillis, currentTime)){
                    val dTime = currentTime - timeInMillis
                    when{
                        dTime > 30L * TimeConstants.DAY ->{
                            return MM_dd_FORMATTER_CN.format(Date(timeInMillis))
                        }
                        dTime > 1L * TimeConstants.DAY ->{
                            return "${dTime / TimeConstants.DAY}天前"
                        }
                        dTime > 1L * TimeConstants.HOUR ->{
                            return "${dTime / TimeConstants.HOUR}小时前"
                        }
                        else -> {
                            return "刚刚"
                        }
                    }
                } else {
                    return yyyy_MM_dd_DATE_FORMATTER_CN.format(Date(timeInMillis))
                }
            }
            return result
        }

        fun getDateTimeClearHMS(timeInMillis :Long): Long {
            val cal = Calendar.getInstance()
            cal.timeInMillis = timeInMillis
            cal[Calendar.HOUR_OF_DAY] = 0
            cal[Calendar.SECOND] = 0
            cal[Calendar.MINUTE] = 0
            cal[Calendar.MILLISECOND] = 0
            return cal.timeInMillis
        }

        fun getTodayWithoutTime(): Calendar {
            val cal = Calendar.getInstance()
            cal[Calendar.HOUR_OF_DAY] = 0
            cal[Calendar.SECOND] = 0
            cal[Calendar.MINUTE] = 0
            cal[Calendar.MILLISECOND] = 0
            return cal
        }

        fun calendar(timeInMillis :Long): Calendar {
            val calendar = Calendar.getInstance()
            calendar.timeInMillis = timeInMillis
            return calendar
        }

        fun calendar(date: Date): Calendar {
            val calendar = Calendar.getInstance()
            calendar.time = date
            return calendar
        }

        /**
         * @param date 日期
         * @return 当月最大天数
         */
        fun maxDaysOfMonth(date: Date): Int {
            return calendar(date).getActualMaximum(Calendar.DATE)
        }

        /**
         * @param date 日期
         * @return 当月第一天在月份表中的索引
         */
        fun firstDayOfMonthIndex(date: Date): Int {
            val calendar = calendar(date)
            calendar[Calendar.DAY_OF_MONTH] = 1
            return calendar[Calendar.DAY_OF_WEEK] - 1
        }

        /**
         * 区间内有多少个月
         *
         * @param sDate 开始日期
         * @param eDate 结束日期
         * @return 月数
         */
        fun months(sDate: Date, eDate: Date): Int {
            val before: Calendar = calendar(min(sDate, eDate))
            val after: Calendar = calendar(max(sDate, eDate))
            val diffYear = after[Calendar.YEAR] - before[Calendar.YEAR]
            val diffMonth = after[Calendar.MONTH] - before[Calendar.MONTH]
            return diffYear * 12 + diffMonth
        }

        fun max(sDate: Date, eDate: Date): Date {
            return if (sDate.time > eDate.time) sDate else eDate
        }

        fun min(sDate: Date, eDate: Date): Date {
            return if (sDate.time > eDate.time) eDate else sDate
        }

        fun millis2FitTimeSpan(timeInMillis: Long): String {
            if (timeInMillis <= 0L) return "00:00"
            var millis = timeInMillis
            val sb = StringBuilder()
            val units = arrayOf(":", ":", "")//"小时", "分钟", "秒"
            val unitLen = intArrayOf( 3600000, 60000, 1000)
            for (i in unitLen.indices) {
                if (millis >= 0) {
                    val mode = millis / unitLen[i]
                    millis -= mode * unitLen[i]
                    if(i >= unitLen.size - 2 || mode > 0){
                        if(mode < 10){
                            sb.append("0").append(mode).append(units[i])
                        }else {
                            sb.append(mode).append(units[i])
                        }

                    }
                }
            }
            return sb.toString()
        }

        fun millis2FitTimeSpan(millis: Long, precision: Int): String? {
            var millis = millis
            var precision = precision
            if (precision <= 0) return null
            precision = Math.min(precision, 5)
            val units = arrayOf("天", "小时", "分钟", "秒", "毫秒")
            if (millis == 0L) return "0${units[precision - 1]}"
            val sb = StringBuilder()
            if (millis < 0) {
                sb.append("-")
                millis = -millis
            }
            val unitLen = intArrayOf(86400000, 3600000, 60000, 1000, 1)
            for (i in 0 until precision) {
                if (millis >= unitLen[i]) {
                    val mode = millis / unitLen[i]
                    millis -= mode * unitLen[i]
                    sb.append(mode).append(units[i])
                }
            }
            return sb.toString()
        }
    }

    //秒表 计时
    class Stopwatch {

        private var startTick: Long = 0
        private var elapsedNanos: Long = 0
        private var hasUsed = false

        fun start(): Stopwatch {
            startTick = System.nanoTime()
            hasUsed = true
            return this
        }

        fun stop(): Stopwatch {
            elapsedNanos = System.nanoTime() - startTick
            return this
        }

        fun restart(): Stopwatch {
            hasUsed = false
            startTick = System.nanoTime()
            return this
        }

        fun getElapsedNanos(): Long {
            return elapsedNanos
        }

        override fun toString(): String {
            val nanos: Long = elapsedNanos
            val unit: TimeUnit = chooseUnit(nanos)
            val value =
                nanos.toDouble() / TimeUnit.NANOSECONDS.convert(1, unit)
            return formatCompact4Digits(value) + " " + abbreviate(unit)
        }

        private fun chooseUnit(nanos: Long): TimeUnit {
            if (TimeUnit.DAYS.convert(nanos, TimeUnit.NANOSECONDS) > 0) {
                return TimeUnit.DAYS
            }
            if (TimeUnit.HOURS.convert(nanos, TimeUnit.NANOSECONDS) > 0) {
                return TimeUnit.HOURS
            }
            if (TimeUnit.MINUTES.convert(nanos, TimeUnit.NANOSECONDS) > 0) {
                return TimeUnit.MINUTES
            }
            if (TimeUnit.SECONDS.convert(nanos, TimeUnit.NANOSECONDS) > 0) {
                return TimeUnit.SECONDS
            }
            if (TimeUnit.MILLISECONDS.convert(nanos, TimeUnit.NANOSECONDS) > 0) {
                return TimeUnit.MILLISECONDS
            }
            return if (TimeUnit.MICROSECONDS.convert(nanos, TimeUnit.NANOSECONDS) > 0) {
                TimeUnit.MICROSECONDS
            } else {
                TimeUnit.NANOSECONDS
            }
        }

        private fun formatCompact4Digits(value: Double): String? {
            return String.format("%.4g", value)
        }

        private fun abbreviate(unit: TimeUnit): String? {
            return when (unit) {
                TimeUnit.NANOSECONDS -> "ns"
                TimeUnit.MICROSECONDS -> "\u03bcs" // μs
                TimeUnit.MILLISECONDS -> "ms"
                TimeUnit.SECONDS -> "s"
                TimeUnit.MINUTES -> "min"
                TimeUnit.HOURS -> "h"
                TimeUnit.DAYS -> "d"
                else -> throw AssertionError()
            }
        }



    }
}